package cn.org.xwb.common.valid.aspect;

import cn.org.xwb.common.model.constants.ResponseConstant;
import cn.org.xwb.common.model.vo.RespResult;
import cn.org.xwb.common.utils.AspectUtil;
import cn.org.xwb.common.utils.ReflectUtil;
import cn.org.xwb.common.valid.annotation.str.StrNotNull;
import cn.org.xwb.common.valid.annotation.str.StrNotNullObj;
import cn.org.xwb.common.valid.annotation.str.StrNotNullParam;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Objects;

/**
 * 判断切点方法中被标注的字符串类型的参数是否都不为空
 */
@Aspect
@Order(2)
@Component
@Log4j2
public class StringNotNullAspect {

    /**
     * 切点
     */
    @Pointcut("@annotation(cn.org.xwb.common.valid.annotation.str.StrNotNull)")
    private void pointCut() {
    }

    /**
     * 判断切点方法中被标注的字符串类型的参数是否都不为空
     *
     * @param proceedingJoinPoint 切点
     * @return 响应结果对象
     */
    @Around("pointCut()")
    public Object judgeStringIsNotNullAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 对每个参数进行判断，字符串类型是否为空
        Object[] results = AspectUtil.doParamHandleBeforeAdvice(
                proceedingJoinPoint,
                StrNotNullObj.class,
                (Object arg) -> {
                    // 判断对象形式参数中的字符串类型字段是否为空
                    // 存在为空 return false，否则 return true
                    // return false - 不是所有字符串非空
                    return !judgeObjAllStrFieldIsNull(arg);
                },
                StrNotNullParam.class,
                (Object arg) -> {
                    // 当前字符串参数为空 return false，否则 return true
                    // return false - 不是所有字符串非空
                    if (Objects.isNull(arg) || ((String) arg).isEmpty()) return false;
                    return true;
                }
        );
        // 如果要求非空的参数中，存在空，返回错误响应结果对象
        // 存在 false - 不是所有字符串非空
        if (
                Arrays.stream(results).anyMatch((result) -> {
                    if (result instanceof Boolean) return (Boolean) result == false;
                    else return false;
                })
        ) {
            log.error("方法 {} 接收的字符串参数中存在空字符串(null或“”)", AspectUtil.getPointMethodSignatureStr(proceedingJoinPoint));
            return RespResult.fail(ResponseConstant.PARAM_IS_NULL);
        }
        // 执行切点方法并返回切点方法的执行结果
        return proceedingJoinPoint.proceed();
    }

    /**
     * 判断对象中的所有字符串类型的被 StringNotNullAnnotation
     * 标注的字段是否存在为空，只要其中一个为空就返回 true
     *
     * @param object 对象
     * @return true 存在空；false 都不为空
     */
    private Boolean judgeObjAllStrFieldIsNull(Object object) {
        try {
            ReflectUtil.doObjAllFieldHandleByAnnotation(
                    object,
                    (Object obj, Field field, Annotation[] annotations) -> {
                        // 当前字段没有被 StringNotNullAnnotation 标注直接判断下一个字段
                        if (!ReflectUtil.judgeContainAnnotation(annotations, StrNotNull.class)) {
                            return;
                        }
                        // 当前字段被 StringNotNullAnnotation 标注，需要进行判断
                        // 获取当前字段的值
                        String fieldValue = null;
                        fieldValue = (String) ReflectUtil.getFieldValue(obj, field);
                        // 当存在字符串为空时，抛出错误
                        if (Objects.isNull(fieldValue) || fieldValue.isEmpty()) {
                            throw new RuntimeException("存在字符串为空");
                        }
                    }
            );
        } catch (Exception e) {
            // 接收到因字符串为空而抛出的错误，对象中的所有字符串类型的被
            // StringNotNullAnnotation 标注的字段存在为空
            return true;
        }
        // 对象中的所有字符串类型的被 StringNotNullAnnotation 标注的字段不存在为空
        return false;
    }

}
